﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EditAnyString.Providers;
using EditAnyString.Extensions;

namespace EditAnyString.EFStorage.Providers
{
	public class EFStorageProvider : IEASStorageProvider
	{
		private const string CACHE_PER_CONTEXT_KEY = "EAS.EF.Context-";

		private readonly IEASCacheProvider _cacheProvider;

		public EFStorageProvider( IEASCacheProvider cacheProvider )
		{
			_cacheProvider = cacheProvider;
		}

		private EditAnyString.Models.EditableString toEditableString( Domain.EAS_EditableString src )
		{
			if( src == null )
				return null;
			return new EditAnyString.Models.EditableString( src.Key,
				src.Context, src.Index, src.Value );
		}

		private IList<EditAnyString.Models.EditableString> toEditableStringList(
			IEnumerable<Domain.EAS_EditableString> src )
		{
			if( src == null )
				return null;
			return src.Select( x => toEditableString( x ) ).ToList();
		}

		#region IEASStorageProvider Members

		public EditAnyString.Models.EditableString GetByKey( int key )
		{
			using( var db = new Domain.EFStorageContainer() )
			{
				var found = db.EAS_EditableStringSet.FirstOrDefault( x => x.Key == key );
				return toEditableString( found );
			}
		}

		public EditAnyString.Models.EditableString GetString( string context, string t, int? idx )
		{
			// we are caching by contexts
			string cacheKey = ( CACHE_PER_CONTEXT_KEY + context ).ToLowerInvariant();
			var stringsInContext = _cacheProvider.Get<Dictionary<int, EditAnyString.Models.EditableString>>( cacheKey, -1,
				() =>
				{
					using( var db = new Domain.EFStorageContainer() )
					{
						// get all elements that are in this context
						var inContext = db.EAS_EditableStringSet.Where( x => x.Context == context );
						return toEditableStringList( inContext ).ToDictionary( x => x.Key );
					}
				} );
			int key = EditAnyString.Models.EditableString.GetKey( context, t, idx );
			// find key in our (possibly cached) list
			EditAnyString.Models.EditableString found = null;
			if( stringsInContext.TryGetValue( key, out found ) )
				return found;
			return null;
		}

		public string Name
		{
			get { return "EAS EPL nHibernate provider"; }
		}

		private IEnumerable<Domain.EAS_EditableString> getByCriteria( IQueryable<Domain.EAS_EditableString> q,
			int? searchKey, string searchContext,
			string searchValue, int pageIndex, int pageSize, out int total )
		{
			if( searchKey != null )
				q = q.Where( x => x.Key == searchKey.Value );
			if( !string.IsNullOrWhiteSpace( searchContext ) )
				q = q.Where( x => x.Context.Contains( searchContext ) );
			if( !string.IsNullOrWhiteSpace( searchValue ) )
				q = q.Where( x => x.Value.Contains( searchValue ) );

			total = q.Count();
			return q.OrderBy( x => x.Key ).Skip( pageIndex * pageSize ).Take( pageSize ).ToList();
		}

		public IList<EditAnyString.Models.EditableString> Search( int? searchKey, string searchContext,
			string searchValue, int pageIndex, int pageSize, out int total )
		{
			using( var db = new Domain.EFStorageContainer() )
			{
				var list = getByCriteria( db.EAS_EditableStringSet, searchKey, searchContext, searchValue, pageIndex, pageSize, out total );
				return toEditableStringList( list );
			}
		}

		public int StoreEASData( EditAnyString.Models.EditableStringDictionary source )
		{
			var allStrings = source.SelectMany( x => x.Value );
			int added = 0;
			using( var db = new Domain.EFStorageContainer() )
			{
				var q = db.EAS_EditableStringSet;
				foreach( var s in allStrings )
				{
					// check if we already have this item - if yes, then ignore
					int key = EditAnyString.Models.EditableString.GetKey( s.Context, s.Value, s.Index ); 
					var found = q.FirstOrDefault( x => x.Key == key );
					if( found != null )
						continue;
					var newString = new Domain.EAS_EditableString()
					{
						Key = key,
						Context = s.Context,
						Value = s.Value,
						Index = s.Index
					};
					q.AddObject( newString );
					added++;
				}
				db.SaveChanges();
			}
			if( added > 0 )
				_cacheProvider.Clear();
			return added;
		}

		public bool Update( int key, string value )
		{
			using( var db = new Domain.EFStorageContainer() )
			{
				var q = db.EAS_EditableStringSet;
				// check if we already have this item
				var found = q.FirstOrDefault( x => x.Key == key );
				if( found == null )
					return false;
				found.Value = value;
				db.SaveChanges();
				// remove conext from cache
				string cacheKey = ( CACHE_PER_CONTEXT_KEY + found.Context ).ToLowerInvariant();
				_cacheProvider.Remove( cacheKey );
			}
			return true;
		}

		#endregion
	}
}
